#pragma once

#include <TH1F.h>

#ifdef WIN32
#include "defines.h"
#endif

#include "CalibData.h"
#include "ValueWithError.h"
#include "ArchiveWrapper.h"
#include "VisualizationWrapper.h"
#include "CalibOptions.h"

#include <sstream>

class TomsonScattering
{
public:
	TomsonScattering():
	   m_laserWavelength(1054), m_signalStart(0.2), m_signalEnd(1.0), m_signal12Start(0.6), m_signal12End(1.6), m_visHelper(NULL), m_arc(NULL),
           m_beforeMaxStart(45), m_beforeMaxEnd(10), m_afterMaxStart(15), m_afterMaxEnd(20), m_chanelNum(12), m_qbitsADT(8), m_syncMaxIdx(-1),
           m_maxOffset(-0.2), m_secPeakOffset(0.2), m_secondPeakStart(1.0), m_secondPeakEnd(1.4), m_beforeSecondPeakStart(30),
           m_beforeSecondPeakEnd(10), m_afterSecondPeakStart(15), m_aftereSecondPeakEnd(15)
	{
               m_chanelEnabled.assign(5, true);
               m_deltaL.assign(5, 7.0);
        };

        // This is the main function - it is called to calculate temperature
        // before calling be sure to set all the needed inputs, e.g. valid ArchiveWrapper object pointer
	void Calculate(CalibData &calibrations, bool useStatWeight = true, bool useFilters = true, bool timeSync = false, bool autoChooseChanels = false);

        // This one is used when background calibration is done, siply calls CalculateSignalIntegrals
        void CalculateBgCalib(std::vector<ValueWithError> &bgCalib, bool useFilters = true, bool timeSync = false);

        // Calculate peak calibration (calc integrals for either first or second peak)
        void CalculatePeakRatioCalib(std::vector<ValueWithError>& peakCalib, calib_options opts = opt_none, bool useFilters = true);

        // Used to normalize peak ration when calibration is performed
        void NormPeakRatioCalib(std::vector<ValueWithError>& peakCalib, calib_options opts);

	void SetArchive(ArchiveWrapper *wrapper) {m_arc = wrapper;};
	void SetChanelStates(std::vector<bool> states){ m_chanelEnabled.assign(states.begin(), states.end()); }; // on/off

	ValueWithError GetTe(bool chanelWidth = true);
	ValueWithError GetNe(bool chanelWidth = true);

	std::vector<std::pair<double, double> > GetLine(bool chanelWidth = true);
	std::vector<std::pair<double, ValueWithError> > GetPoints(bool chanelWidth = true);

        void SetSignalTimes( double start, double end, double startChanel12, double endChanel12 )
        {
            m_signalStart = start;
            m_signalEnd = end;
            m_signal12Start = startChanel12;
            m_signal12End = endChanel12;
        };

        void SetOffsets(double beforeMaxStart, double beforeMaxEnd, double afterMaxStart, double afterMaxEnd)
        {
            m_beforeMaxStart = beforeMaxStart;
            m_beforeMaxEnd = beforeMaxEnd;
            m_afterMaxStart = afterMaxStart;
            m_afterMaxEnd = afterMaxEnd;
        }

        void SetTimeSyncSignalOffset(double offset) { m_maxOffset = offset; }
        void SetTimeSyncSecondPeakOffset(double offset) { m_secPeakOffset = offset; }

        void SetSecondPeakTimes( double start, double end )
        {
            m_secondPeakStart = start;
            m_secondPeakEnd = end;
        }

        void SetSecondPeakOffsets( double beforeSecPeakStart, double beforeSecPeakEnd, double afterSecPeakStart, double afterSecPeakEnd )
        {
            m_beforeSecondPeakStart = beforeSecPeakStart;
            m_beforeSecondPeakEnd = beforeSecPeakEnd;
            m_afterSecondPeakStart = afterSecPeakStart;
            m_aftereSecondPeakEnd = afterSecPeakEnd;
        }

        void SetPeakCalibChanel(int chanel) { m_peakCalibChanel = chanel; };
        void SetDelta(std::vector<double> &vals) { m_deltaL.assign(vals.begin(), vals.end()); }
        void SetChanelNum(int chN) { m_chanelNum = chN; }
        void SetVisHelper(VisualizationWrapper *visHelper) { m_visHelper = visHelper; }
        void SetADTBitCount( int bitCount ) { m_qbitsADT = bitCount; }

private:
	// main functions of the method

        // Calculated integrals and normalizes them to energy integral, does not take calibrations into account
	void CalculateSignalIntegrals(std::vector<ValueWithError> &integralPeakNoCalib, bool useFilters, bool timeSync, bool autoChooseChanels);

        // Applies calibrations to calculated integral and then calculates Te, Ne, etc.
	void CalculateShotParams(CalibData &calibData, std::vector<ValueWithError> &integralWithoutCalib, bool useStatWeight);

	// helper functions
        void LeastSquaresMethod(std::vector<double> &chanelCenter, std::vector<double> &logIntegralPeak, std::vector<double> &statWeight, std::vector<double> &wavelengthDelta, ValueWithError *linePars);
	void BubbleSort(double* tmp);
	int MedianLowpassFilter(std::vector<double> &signal, unsigned int windowSize = 3, double lowPassCoef = 0.3);
	void IntegrateSignal(std::vector<double> &signal, ValueWithError noiseLevel, double amplification);
	void CalcAverageAndError(int start, int stop, std::vector<double> &signal, ValueWithError &average);
	void ReadSignal(TH1F* h, std::vector<double> &signal);
	int FindMaxValueIndex(std::vector<double> &signal, int start, int end, bool timeSync);
	TH1F* GetSignalData( int chanelNum, long int maxI, double &tquant, double &minY, double &maxY, double &amplification );

        // Here all parameters of interest are calculated: Te, Ne, line, points
	void CalcParams(ValueWithError &Te, ValueWithError &Ne, std::vector<std::pair<double, double> > &line,
        std::vector<std::pair<double, ValueWithError> > &points, double alpha, std::vector<double> &chanelCenter,
        std::vector<double> &logIntegralPeak, std::vector<double> &statWeight, double Ne_coef, bool useStatWeight);

        // Calculates corrected integral logarithms, corection is based on width of chanels
	void CalcIntegralCorrection(std::vector<double> &logIntegralPeakCAL, double tetaRad, std::vector<double> lchanel, std::vector<double> integralPeak);

        bool CheckChanel(std::vector<double> &signal, int maxIdx, int chanelNum);
        void CheckConfig();
        void CleanUp();
private:
	std::vector<bool> m_chanelEnabled;
        int m_chanelNum;
	
	// chanel width not concidered
	ValueWithError m_Te;
	ValueWithError m_Ne;

	// chanel width concidered
	ValueWithError m_Te_chanelWidth;
	ValueWithError m_Ne_chanelWidth;

	std::vector<std::pair<double, double> > m_line;
	std::vector<std::pair<double, double> > m_line_chanelWidth;

	std::vector<std::pair<double, ValueWithError> > m_points;
	std::vector<std::pair<double, ValueWithError> > m_points_chanelWidth;

	double m_laserWavelength;//dlina volni neodim. lazera

	ArchiveWrapper *m_arc;
        VisualizationWrapper *m_visHelper;

        double m_signalStart;
        double m_signalEnd;
        double m_signal12Start;
        double m_signal12End;

        double m_beforeMaxStart;
        double m_beforeMaxEnd;
        double m_afterMaxStart;
        double m_afterMaxEnd;

        int m_peakCalibChanel;

        double m_secondPeakStart;
        double m_secondPeakEnd;
        
        double m_beforeSecondPeakStart;
        double m_beforeSecondPeakEnd;
        double m_afterSecondPeakStart;
        double m_aftereSecondPeakEnd;

        std::vector<double> m_deltaL;

        int m_qbitsADT; // number of bits in ADT, can be changed in config file
        int m_syncMaxIdx;
        double m_maxOffset;
        double m_secPeakOffset;

        std::stringstream m_errors;
};
